package com.fuluwa.domain.order.model;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.annotations.ForeignKey;

import com.fuluwa.domain.product.model.Product;
import com.fuluwa.domain.product.model.Product.ProductStatus;
import com.fuluwa.domain.shared.AbstractIdDomainObject;
import com.fuluwa.infra.context.UserUtils;
import com.fuluwa.infra.exception.PersistenceException;

@Entity
@Table(name = "t_order")
public class Order extends AbstractIdDomainObject {

	private static final long serialVersionUID = -1456849377397169029L;

	@Transient
	private static final TotalPriceStrategy TOTAL_PRICE_STRATEGY = new DefaultTotalPriceStrategy();

	private String number;

	@Temporal(TemporalType.TIMESTAMP)
	private Date createTime = new Date();

	private String creator;

	private String seller;
	@Column(name = "f_total_price", columnDefinition = "decimal(13,2)")
	private double totalPrice = 0.0d;

	@OneToMany(mappedBy = "order")
	private Set<OrderItem> orderItems;

	// private long workflowId;
	@Transient
	private OrderStateMachine stateMachine = new OrderStateMachine();
	@Enumerated(EnumType.STRING)
	private OrderState state = OrderState.valueOf(this.stateMachine.getCurrentState());

	private boolean appraise = false; // 是否评价
	@ManyToOne
	@ForeignKey(name="null")
	private PaymentInfo paymentInfo; // 支付信息

	// private OrderInvoice orderInvoice; // 发票信息

	private String remark;

	private Order() {
	}

	public Order(Long id) {
		super(id);
	}

	public Order(String seller, PaymentInfo paymentInfo, String remark) {
		this.seller = seller;
		this.paymentInfo = paymentInfo;
		this.remark = remark;
		paymentInfo.updateOrder(this);
	}

	public Order(String seller, String remark) {
		this.seller = seller;
		this.remark = remark;
	}

	public void setTotalPriceStrategy(TotalPriceStrategy strategy) {
		this.totalPrice = strategy.getTotalPrice(this);
	}

	public Set<OrderItem> getOrderItems() {
		return orderItems;
	}
    public void addItem(Set<OrderItem> items) {
        if (this.orderItems == null) {
            this.orderItems = new HashSet<OrderItem>();
        }
        this.orderItems.addAll(items);
        for (OrderItem orderItem : items) {
        	orderItem.updateOrder(this);
		}
        this.totalPrice = TOTAL_PRICE_STRATEGY.getTotalPrice(this);
    }
	public void addItem(OrderItem item) {
		if (this.orderItems == null) {
			this.orderItems = new HashSet<OrderItem>();
		}
		item.updateOrder(this);
		this.orderItems.add(item);
		this.totalPrice = TOTAL_PRICE_STRATEGY.getTotalPrice(this);
	}

	/*-
	 public void copyProperties(Order orderTemplet) {

	 this.creator = orderTemplet.getCreator();
	 this.createTime = orderTemplet.getCreateTime();
	 this.remark = orderTemplet.getRemark();

	 PaymentInfo paymentInfo = orderTemplet.getPaymentInfo().copyPaymentInfo();
	 this.paymentInfo = paymentInfo;
	 paymentInfo.setOrder(this);

	 DeliveryInfo deliveryInfo = orderTemplet.getDeliveryInfo().copyDeliPaymentInfo();
	 this.deliveryInfo = deliveryInfo;
	 deliveryInfo.setOrder(this);

	 if(orderTemplet.getOrderInvoice() != null){
	 OrderInvoice orderInvoice = orderTemplet.getOrderInvoice().copyOrderInvoice();
	 this.orderInvoice = orderInvoice;
	 orderInvoice.setOrder(this);
	 }
	 }


	 public List<Product> getOutOfStockProducts(){
	 List<Product> products = this.getAllProducts();
	 List<Product> outOfStockProducts = new ArrayList<Product>();
	 for (Product product : products){
	 if (product.getStock() == ProductStockType.OUT_OF_STOCK){
	 outOfStockProducts.add(product);
	 }
	 }
	 return outOfStockProducts;
	 }
	 */

	public List<Product> getAllProducts() {
		List<Product> products = new ArrayList<Product>();
		if (orderItems == null) {
			return products;
		}
		for (OrderItem orderItem : orderItems) {
			products.add(orderItem.getProduct());
		}
		return products;
	}

	public List<Product> getOffSaleProducts() {
		List<Product> products = this.getAllProducts();
		List<Product> offSaleProducts = new ArrayList<Product>();
		for (Product product : products) {
			if (product.getStatus().equals(ProductStatus.OFFLINE)) {
				offSaleProducts.add(product);
			}
		}
		return offSaleProducts;
	}

	/**
	 * added this method to delegate state manipulation to the state machine
	 * 
	 * @throws PersistenceException
	 */
	public void confirm() throws PersistenceException {
		this.stateMachine.triggerConfirm();
		updateState();
	}

	/**
	 * added this method to delegate state manipulation to the state machine
	 * 
	 * @throws PersistenceException
	 */
	public void payment() throws PersistenceException {
		this.stateMachine.triggerPayment();
		updateState();
	}

	/**
	 * added this method to delegate state manipulation to the state machine
	 * 
	 * @throws PersistenceException
	 */
	public void cancel() throws PersistenceException {
		this.stateMachine.triggerCancel();
		updateState();
	}

	/**
	 * added this method to delegate state manipulation to the state machine
	 * 
	 * @throws PersistenceException
	 */
	public void complete() throws PersistenceException {
		this.stateMachine.triggerComplete();
		updateState();
	}

	private void updateState() throws PersistenceException {
		state = OrderState.valueOf(this.stateMachine.getCurrentState());
		this.update();
	}

	/**
	 * originally generated method. note JPA will use property based access
	 * because the annotation is on the setter! private, so that JPA can still
	 * call it. but we dont want anyone else using it!
	 */
	@SuppressWarnings("unused")
	private void setState(String state) {
		if (!this.stateMachine.getCurrentState().equals(state)) {
			this.stateMachine = new OrderStateMachine(state);
		}
	}
	@Override
	public Order load() throws PersistenceException {
		Order o= super.load();
		o.setState(o.getState().toString());
		return o;
	}
	private static class DefaultTotalPriceStrategy implements TotalPriceStrategy {

		public double getTotalPrice(Order order) {
			Set<OrderItem> items = order.getOrderItems();
			double totalPrice = 0;
			if (items != null && items.size() > 0) {
				for (OrderItem item : items) {
					totalPrice += item.getPrice() * item.getQuantity();
				}
			}
			return totalPrice;
		}

	}

	public String getNumber() {
		return number;
	}

	public Date getCreateTime() {
		return createTime;
	}

	public String getCreator() {
		return creator;
	}

	public String getSeller() {
		return seller;
	}

	public double getTotalPrice() {
		return totalPrice;
	}

	public OrderState getState() {
		return state;
	}

	public boolean isAppraise() {
		return appraise;
	}

	public PaymentInfo getPaymentInfo() {
		return paymentInfo;
	}

	public String getRemark() {
		return remark;
	}

	@Override
	public void save() throws PersistenceException {
		this.creator = UserUtils.getCurrentUserName();
		super.save();
		this.number = "O" + StringUtils.leftPad(getId() + "", 10, "0");
		this.update();
	}
}
